`timescale 1ns / 1ps

`include "data_width.vh"

module get_edge_info #(parameter
    TOT_EDGE_MASK_WIDTH = `TOT_EDGE_MASK_WIDTH,
    TOT_ACC_ID_WIDTH    = `TOT_ACC_ID_WIDTH,
    VERTEX_MASK_WIDTH   = `VERTEX_MASK_WIDTH,
    DST_ID_DWIDTH       = `DST_ID_DWIDTH,
    SRC_ID_DWIDTH       = `SRC_ID_DWIDTH,
    VERTEX_PIPE_NUM     = `VERTEX_PIPE_NUM,
    EDGE_PIPE_NUM       = `EDGE_PIPE_NUM,
    MEM_DWIDTH          = `MEM_DWIDTH
    ) (
        input                                                   clk,
        input                                                   front_rst,
        input [MEM_DWIDTH * 2 - 1 : 0]                          mem_data,
        input                                                   mem_data_valid,
        input                                                   front_finish_read,
        input                                                   front_any_dst_data_valid,
        input [TOT_EDGE_MASK_WIDTH - 1 : 0]                     front_tot_src_p_mask,
        input [TOT_ACC_ID_WIDTH - 1 : 0]                        front_tot_acc_id,
        input [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]         front_dst_id,
        input [VERTEX_MASK_WIDTH * VERTEX_PIPE_NUM - 1 : 0]     front_src_p_mask_r,
        input [VERTEX_PIPE_NUM - 1 : 0]                         front_dst_data_valid,
        input                                                   back_stage_any_full,

        output                                                  rst,
        output                                                  rd_plus_valid,
        output                                                  buffer_full_vertex,
        output [SRC_ID_DWIDTH * EDGE_PIPE_NUM - 1 : 0]          src_id,
        output                                                  src_data_valid,
        output [TOT_EDGE_MASK_WIDTH - 1 : 0]                    tot_src_p_mask,
        output [TOT_ACC_ID_WIDTH - 1 : 0]                       tot_acc_id,
        output                                                  any_dst_data_valid,
        output [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]        dst_id,
        output [VERTEX_MASK_WIDTH * VERTEX_PIPE_NUM - 1 : 0]    src_p_mask_r,
        output [VERTEX_PIPE_NUM - 1 : 0]                        dst_data_valid);

    wire                            edge_read_next_valid, vertex_read_next_valid;
    wire                            finish_read;
    wire                            edge_buffer_empty, edge_buffer_full;
    wire                            mask_buffer_empty, mask_buffer_full;
    wire [VERTEX_PIPE_NUM - 1 : 0]  dst_buffer_empty, dst_buffer_full;
    wire                            fr_buffer_empty, fr_buffer_full;
    wire [VERTEX_PIPE_NUM - 1 : 0]  data_valid;

    assign buffer_full_vertex = dst_buffer_full[0];
    assign any_dst_data_valid = data_valid[0];

    get_edge_info_para_trans P (
        .clk(clk), .front_rst(front_rst),

        .rst(rst));

    get_edge_info_edge E1 (
        .clk(clk), .rst(front_rst),
        .mem_data(mem_data), .mem_data_valid(mem_data_valid),
        .edge_read_next_valid(edge_read_next_valid), .vertex_read_next_valid(vertex_read_next_valid),

        .buffer_empty(edge_buffer_empty), .buffer_full(edge_buffer_full),
        .src_id(src_id), .src_data_valid(src_data_valid), .rd_plus_valid(rd_plus_valid));

    get_edge_info_finish_read FR1 (
        .clk                        (clk),
        .rst                        (front_rst),
        .front_finish_read          (front_finish_read),
        .front_any_dst_data_valid   (front_any_dst_data_valid),
        .vertex_read_next_valid     (vertex_read_next_valid),

        .buffer_empty               (fr_buffer_empty),
        .buffer_full                (fr_buffer_full),
        .finish_read                (finish_read));

    get_edge_info_mask M1 (
        .clk(clk), .rst(front_rst),
        .front_tot_src_p_mask(front_tot_src_p_mask), .front_tot_acc_id(front_tot_acc_id),
        .front_any_dst_data_valid(front_any_dst_data_valid), .vertex_read_next_valid(vertex_read_next_valid),

        .buffer_empty(mask_buffer_empty), .buffer_full(mask_buffer_full),
        .tot_src_p_mask(tot_src_p_mask), .tot_acc_id(tot_acc_id));

    generate
        genvar i;
        for (i = 0; i < VERTEX_PIPE_NUM; i = i + 1) begin : M7_BLOCK_1
            get_edge_info_vertex_single V (
                .clk                        (clk),
                .rst                        (front_rst),
                .front_dst_id               (front_dst_id[(i + 1) * DST_ID_DWIDTH - 1 : i * DST_ID_DWIDTH]),
                .front_src_p_mask_r         (front_src_p_mask_r[(i + 1) * VERTEX_MASK_WIDTH - 1 : i * VERTEX_MASK_WIDTH]),
                .front_dst_data_valid       (front_dst_data_valid[i]),
                .front_any_dst_data_valid   (front_any_dst_data_valid),
                .vertex_read_next_valid     (vertex_read_next_valid),

                .buffer_empty               (dst_buffer_empty[i]),
                .buffer_full                (dst_buffer_full[i]),
                .data_valid                 (data_valid[i]),
                .dst_id                     (dst_id[(i + 1) * DST_ID_DWIDTH - 1 : i * DST_ID_DWIDTH]),
                .src_p_mask_r               (src_p_mask_r[(i + 1) * VERTEX_MASK_WIDTH - 1 : i * VERTEX_MASK_WIDTH]),
                .dst_data_valid             (dst_data_valid[i]));
        end
    endgenerate

    get_edge_info_control C1 (
        .edge_buffer_empty      (edge_buffer_empty),
        .dst_buffer_empty       (dst_buffer_empty[0]),
        .fr_buffer_empty        (fr_buffer_empty),
        .back_stage_any_full    (back_stage_any_full),
        .finish_read            (finish_read),

        .edge_read_next_valid   (edge_read_next_valid),
        .vertex_read_next_valid (vertex_read_next_valid));

endmodule

module get_edge_info_para_trans (
    input       clk,
    input       front_rst,

    output reg  rst);

    always @ (posedge clk) begin
        rst <= front_rst;
    end

endmodule

module get_edge_info_edge #(parameter
    MEM_DWIDTH      = `MEM_DWIDTH,
    SRC_ID_DWIDTH   = `SRC_ID_DWIDTH,
    EDGE_PIPE_NUM   = `EDGE_PIPE_NUM
    ) (
        input                                               clk,
        input                                               rst,
        input [MEM_DWIDTH * 2 - 1 : 0]                      mem_data,
        input                                               mem_data_valid,
        input                                               edge_read_next_valid,
        input                                               vertex_read_next_valid,

        output                                              buffer_empty,
        output                                              buffer_full,
        output reg [SRC_ID_DWIDTH * EDGE_PIPE_NUM - 1 : 0]  src_id,
        output reg                                          src_data_valid,
        output reg                                          rd_plus_valid);

    wire [SRC_ID_DWIDTH * EDGE_PIPE_NUM - 1 : 0] top_src_id;
    // change vertex without change edge, so fifo need fall through.
    // high
    mem_data_fifo_fall_through mdf1 (
        .clk(clk), .srst(rst),
        .din(mem_data[MEM_DWIDTH * 2 - 1 : MEM_DWIDTH]),
        .wr_en(mem_data_valid), .rd_en(edge_read_next_valid),

        .dout(top_src_id[MEM_DWIDTH * 2 - 1 : MEM_DWIDTH]),
        .empty(buffer_empty), .prog_full(buffer_full));

    // low
    mem_data_fifo_fall_through mdf2 (
        .clk(clk), .srst(rst),
        .din(mem_data[MEM_DWIDTH - 1 : 0]),
        .wr_en(mem_data_valid), .rd_en(edge_read_next_valid),

        .dout(top_src_id[MEM_DWIDTH - 1 : 0]));

    always @ (posedge clk) begin
        if (rst) begin
            src_id          <= 0;
            src_data_valid  <= 1'b0;
            rd_plus_valid   <= 1'b0;
        end
        else begin
            src_id          <= top_src_id;
            rd_plus_valid   <= edge_read_next_valid;
            src_data_valid  <= vertex_read_next_valid;
        end
    end

endmodule

module get_edge_info_finish_read (
    input clk,
    input rst,
    input front_finish_read,
    input front_any_dst_data_valid,
    input vertex_read_next_valid,

    output buffer_empty,
    output buffer_full,
    output finish_read);

    wire top_finish_read;
    wire valid;
    // first work fall through
    finish_read_fifo_fall_through frf (
        .clk    (clk),
        .srst   (rst),
        .din    (front_finish_read),
        .wr_en  (front_any_dst_data_valid),
        .rd_en  (vertex_read_next_valid),

        .dout   (top_finish_read),
        .empty  (buffer_empty),
        .full   (buffer_full),
        .valid  (valid));

    assign finish_read = top_finish_read && valid;

endmodule

module get_edge_info_mask #(parameter
    TOT_EDGE_MASK_WIDTH = `TOT_EDGE_MASK_WIDTH, TOT_ACC_ID_WIDTH = `TOT_ACC_ID_WIDTH
    ) (
        input                                   clk,
        input                                   rst,
        input [TOT_EDGE_MASK_WIDTH - 1 : 0]     front_tot_src_p_mask,
        input [TOT_ACC_ID_WIDTH - 1 : 0]        front_tot_acc_id,
        input                                   front_any_dst_data_valid,
        input                                   vertex_read_next_valid,

        output                                  buffer_empty,
        output                                  buffer_full,
        output [TOT_EDGE_MASK_WIDTH - 1 : 0]    tot_src_p_mask,
        output [TOT_ACC_ID_WIDTH - 1 : 0]       tot_acc_id);

    tot_edge_mask_fifo TEM1 (
        .clk(clk), .srst(rst),
        .din(front_tot_src_p_mask), .wr_en(front_any_dst_data_valid), .rd_en(vertex_read_next_valid),

        .dout(tot_src_p_mask), .empty(buffer_empty), .prog_full(buffer_full));

    tot_acc_id_fifo TAI1 (
        .clk(clk), .srst(rst),
        .din(front_tot_acc_id), .wr_en(front_any_dst_data_valid), .rd_en(vertex_read_next_valid),

        .dout(tot_acc_id));

endmodule

module get_edge_info_vertex_single #(parameter
    DST_ID_DWIDTH = `DST_ID_DWIDTH, VERTEX_MASK_WIDTH = `VERTEX_MASK_WIDTH
) (
    input                               clk,
    input                               rst,
    input [DST_ID_DWIDTH - 1 : 0]       front_dst_id,
    input [VERTEX_MASK_WIDTH - 1 : 0]   front_src_p_mask_r,
    input                               front_dst_data_valid,
    input                               front_any_dst_data_valid,
    input                               vertex_read_next_valid,

    output                              buffer_empty,
    output                              buffer_full,
    output                              data_valid,
    output [DST_ID_DWIDTH - 1 : 0]      dst_id,
    output [VERTEX_MASK_WIDTH - 1 : 0]  src_p_mask_r,
    output                              dst_data_valid);

    dst_id_fifo DI1 (
        .clk(clk), .srst(rst),
        .din(front_dst_id), .wr_en(front_any_dst_data_valid), .rd_en(vertex_read_next_valid),

        .dout(dst_id), .empty(buffer_empty), .prog_full(buffer_full));

    vertex_mask_fifo VM1 (
        .clk(clk), .srst(rst),
        .din(front_src_p_mask_r), .wr_en(front_any_dst_data_valid), .rd_en(vertex_read_next_valid),

        .dout(src_p_mask_r));

    valid_fifo DDV1 (
        .clk(clk), .srst(rst),
        .din(front_dst_data_valid), .wr_en(front_any_dst_data_valid), .rd_en(vertex_read_next_valid),

        .dout(dst_data_valid), .valid(data_valid));

endmodule

module get_edge_info_control (
    input edge_buffer_empty,
    input dst_buffer_empty,
    input fr_buffer_empty,
    input back_stage_any_full,
    input finish_read,

    output edge_read_next_valid,
    output vertex_read_next_valid);

    assign vertex_read_next_valid = !(edge_buffer_empty || dst_buffer_empty || back_stage_any_full || fr_buffer_empty);
    assign edge_read_next_valid = (!(edge_buffer_empty || dst_buffer_empty || back_stage_any_full || fr_buffer_empty) && finish_read);

endmodule